package chap_06;

import chap_04.Dish;
import org.junit.jupiter.api.Test;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * 分区
 * <p>
 * 分区是分组的特殊情况：由一个谓词（返回一个布尔值的函数）作为分类函数，它称分区函
 * 数。分区函数返回一个布尔值，这意味着得到的分组Map的键类型是Boolean，于是它最多可以
 * 分为两组——true是一组，false是一组。
 */
public class Run_03 {

    List<Dish> menu = Arrays.asList(
            new Dish("pork", false, 800, Dish.Type.MEAT),
            new Dish("beef", false, 700, Dish.Type.MEAT),
            new Dish("chicken", false, 400, Dish.Type.MEAT),
            new Dish("french fries", true, 530, Dish.Type.OTHER),
            new Dish("rice", true, 350, Dish.Type.OTHER),
            new Dish("season fruit", true, 120, Dish.Type.OTHER),
            new Dish("pizza", true, 550, Dish.Type.OTHER),
            new Dish("prawns", false, 300, Dish.Type.FISH),
            new Dish("salmon", false, 450, Dish.Type.FISH));

    /**
     * 素食和非素食分开
     */
    @Test
    public void demo_01() {
        Map<Boolean, List<Dish>> collect = menu.stream()
                .collect(Collectors.partitioningBy(Dish::isVegetarian));

        System.out.println(collect);
    }

    @Test
    public void demo_02() {
        Map<Boolean, Map<Dish.Type, List<Dish>>> collect = menu.stream()
                .collect(Collectors.partitioningBy(Dish::isVegetarian, Collectors.groupingBy(Dish::getType)));

        System.out.println(collect);

    }

    @Test
    public void demo_03() {
        Map<Boolean, Dish> collect = menu.stream()
                .collect(Collectors.partitioningBy(
                        Dish::isVegetarian,
                        Collectors.collectingAndThen(
                                Collectors.maxBy(
                                        Comparator.comparing(Dish::getCalories)),
                                Optional::get
                        )
                        )
                );

        System.out.println(collect);
    }

    @Test
    public void demo_04() {
        Map<Boolean, Map<Boolean, List<Dish>>> collect = menu.stream()
                .collect(Collectors.partitioningBy(Dish::isVegetarian,
                        Collectors.partitioningBy((Dish d) -> d.getCalories() > 500)));

        System.out.println(collect);
    }

    @Test
    public void demo_05() {
        Map<Boolean, Long> collect = menu.stream()
                .collect(Collectors.partitioningBy(Dish::isVegetarian, Collectors.counting()));

        System.out.println(collect);
    }

    /**
     * 将数字按质数和非质数分区
     */
    @Test
    public void demo_06() {
        System.out.println(partitionPrimes(10));
    }

    public boolean isPrime(int candidate) {
        int candidateRoot = (int) Math.sqrt((double) candidate);
        return IntStream.rangeClosed(2, candidateRoot)
                .noneMatch(i -> candidate % i == 0);
    }

    public Map<Boolean, List<Integer>> partitionPrimes(int n) {
        return IntStream.rangeClosed(2, n).boxed()
                .collect(Collectors.partitioningBy(i -> isPrime(i)));
    }
}
